package com.ak.filter;

import com.ak.entity.GatewayConstant;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * <h1>全局缓存请求Body过滤器</h1>
 * * 将请求数据缓存方便后面的过滤器拿取
 *
 * @author xk
 * @create 2023/12/6 14:27
 * @since 1.0.0
 */
public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange
                .getRequest()
                .getURI()
                .getPath();
        boolean isLoginOrRegister =
                path.contains(GatewayConstant.LOGIN_URL) || path.contains(GatewayConstant.REGISTER_URL);
        // 如果没有请求头或者不是登录注册接口，不用去缓存了，往下走下面的过滤器
        if (null == exchange.getRequest().getHeaders().getContentType() || !isLoginOrRegister) {
            return chain.filter(exchange);
        }
        return DataBufferUtils
                .join(exchange.getRequest().getBody()) //获取请求数据
                .flatMap(dataBuffer -> {
                    // 确保数据缓冲区不被释放, 必须要 DataBufferUtils.retain
                    DataBufferUtils.retain(dataBuffer);
                    // defer、just 都是去创建数据源, 得到当前数据的副本
                    Flux<DataBuffer> cachedFlux = Flux.defer(() ->
                            Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
                    // 重新包装 ServerHttpRequest, 重写 getBody 方法, 能够返回请求数据
                    ServerHttpRequest mutatedRequest =
                            new ServerHttpRequestDecorator(exchange.getRequest()) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    return cachedFlux;
                                }
                            };
                    // 将包装之后的 ServerHttpRequest 向下继续传递
                    return chain.filter(exchange.mutate().request(mutatedRequest).build());
                });
    }

    /**
     * 下面过滤器要拿到当前过滤器缓存内容优先级要比当前返回数值要大
     *
     * @return
     */
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 1;
    }
}
